package com.tian.service.impl.market;

import com.alibaba.fastjson.JSON;
import com.tian.common.exception.BusinessException;
import com.tian.common.util.ChargeMarketRedisKeyPrefix;
import com.tian.common.util.CommonResult;
import com.tian.common.util.RedisConstantPre;
import com.tian.dto.market.coupon.CouponUsedStatisticsRespDto;
import com.tian.dto.market.coupon.UserCouponCacheDto;
import com.tian.entity.ChargeCoupon;
import com.tian.entity.ChargeUserCoupon;
import com.tian.entity.CouponUsedStatistics;
import com.tian.mapper.ChargeCouponMapper;
import com.tian.mapper.ChargeUserCouponMapper;
import com.tian.mapper.CouponUsedStatisticsMapper;
import com.tian.mq.message.CouponUsedStatisticsMessage;
import com.tian.service.market.CouponUsedStatisticsService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toList;

/**
 * {@code @description:} TODO
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-10-26 11:25
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class CouponUsedStatisticsServiceImpl implements CouponUsedStatisticsService {
    @Resource
    private ChargeUserCouponMapper chargeUserCouponMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private CouponUsedStatisticsMapper couponUsedStatisticsMapper;
    @Resource
    private ChargeCouponMapper chargeCouponMapper;

    @Override
    public void statistics(CouponUsedStatisticsMessage couponUsedStatisticsMessage) {
        Long orderId = couponUsedStatisticsMessage.getOrderId();

        int count = couponUsedStatisticsMapper.countByOrderId(orderId);
        // 判断优惠券使用统计是否已存在，防止重复统计
        if (count > 0) {
            log.error("订单id={} 优惠券使用统计已存在", couponUsedStatisticsMessage.getOrderId());
            return ;
        }
        List<ChargeUserCoupon> chargeUserCoupons = chargeUserCouponMapper.selectByOrderId(orderId);

        List<Long> userCcouponIdList = chargeUserCoupons.stream().map(ChargeUserCoupon::getId).collect(toList());
        Map<Integer, BigDecimal> mapCache = new HashMap<>();
        calculateCouponAmount(mapCache, couponUsedStatisticsMessage.getOrderAmount(), userCcouponIdList);
         // 计算优惠券使用金额
        List<CouponUsedStatistics> couponUsedStatisticsList = new ArrayList<>();
        for (Integer couponId : mapCache.keySet()) {
            BigDecimal couponAmount = mapCache.get(couponId);
            CouponUsedStatistics couponUsedStatistics = new CouponUsedStatistics();
            couponUsedStatistics.setCouponId(couponId);
            couponUsedStatistics.setOrderId(orderId);
            couponUsedStatistics.setDiscountAmount(couponAmount);
            couponUsedStatisticsList.add(couponUsedStatistics);
        }
        if(CollectionUtils.isEmpty(couponUsedStatisticsList)){
            return;
        }
        //批量插入优惠券使用统计
        couponUsedStatisticsMapper.insertBatch(couponUsedStatisticsList);
        log.info("订单id={} 优惠券使用统计完成", couponUsedStatisticsMessage.getOrderId());
    }

    private void calculateCouponAmount(Map<Integer, BigDecimal> couponAmountMap, BigDecimal amount, List<Long> userCouponIdList) {
        for (Long userCouponId : userCouponIdList) {
            //获取用户优惠券记录信息
            RBucket<String> userCouponBucket = redissonClient.getBucket(ChargeMarketRedisKeyPrefix.CHARGE_USER_COUPON_KEY + userCouponId);
            UserCouponCacheDto userCouponCacheDto = JSON.parseObject(userCouponBucket.get(), UserCouponCacheDto.class);
            Integer couponId = userCouponCacheDto.getCouponId();
            //获取优惠券信息
            ChargeCoupon coupon = chargeCouponMapper.selectByPrimaryKey(couponId);
            //优惠金额
            BigDecimal couponAmount = coupon.getFaceValue();
            //优惠券类型 1:满减券 2:折扣券 3:无门槛券（直接抵扣）
            Integer type = coupon.getUseType();
            if (type == 1) {
                //判断是否满足满减条件
                //比如设置的是满100元减10，那这个amount就必须大于等于100元才减去10元
                if (amount.compareTo(couponAmount) < 0) {
                    throw new BusinessException("优惠券不满足满减条件");
                }
                couponAmountMap.put(couponId, couponAmount);
            } else if (type == 2) {
                // 计算折扣金额（面值乘以100以表示百分比）
                BigDecimal discountAmount = couponAmount.multiply(new BigDecimal("100"));
                couponAmount = amount.multiply(discountAmount).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);

                // 从原始金额中减去计算后的折扣金额（即减少对应的比例金额）
                BigDecimal result = amount.subtract(couponAmount).setScale(2, RoundingMode.HALF_UP);
                //实际优惠金额
                BigDecimal result3 = amount.multiply(result);
                couponAmountMap.put(couponId, result3);

            } else if (type == 3) {
                // 无门槛券直接抵扣
                couponAmountMap.put(couponId, couponAmount);
            } else {
                log.error("优惠券类型错误, couponId={}`, type={}",  couponId, type);
            }
        }
    }

    @Override
    public CommonResult<CouponUsedStatisticsRespDto> getCouponUsedStatistics(Integer couponId) {
        //统计每个优惠券被使用次数
        RAtomicLong atomicLong = redissonClient.getAtomicLong(RedisConstantPre.COUPON_GET_COUNT_PRE + couponId);
        CouponUsedStatisticsRespDto couponUsedStatisticsRespDto = couponUsedStatisticsMapper.countByCouponId(couponId);

        if (couponUsedStatisticsRespDto == null) {
            couponUsedStatisticsRespDto = new CouponUsedStatisticsRespDto();
            couponUsedStatisticsRespDto.setCouponId(couponId);
            couponUsedStatisticsRespDto.setCountUsed(0);
            couponUsedStatisticsRespDto.setDiscountTotalAmount(BigDecimal.ZERO);
        }
        couponUsedStatisticsRespDto.setCountGet(atomicLong.get());
        return CommonResult.success(couponUsedStatisticsRespDto);
    }
}
